package nsoft.swing.engine;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.JComponent;
import javax.swing.JPanel;

import nsoft.swing.annotation.AComponent;
import nsoft.swing.annotation.APanel;
import nsoft.swing.annotation.Type;
import nsoft.swing.layout.BorderLayoutHelper;
import nsoft.swing.layout.FlexiGridLayoutHelper;
import nsoft.swing.layout.FlowLayoutHelper;
import nsoft.swing.layout.LabelComponentLayoutHelper;
import nsoft.swing.layout.LayoutHelper;
import nsoft.util.BeanUtilities;
import nsoft.util.Property;
import nsoft.util.UiModel;

public class ContainerFactory {
	private static final String LABEL_COMPONENT_LAYOUT = "LabelComponentLayout";

	private static final String BORDER_LAYOUT = "BorderLayout";

	private static final String FLOW_LAYOUT = "FlowLayout";
	
	private static final String FLEXIGRID_LAYOUT = "FlexiGridLayout";

	private static final String SWINGETS_LAYOUT_HELPER = "SwingetsLayoutHelper";

	private IComponentFactory componentFactory;

	private Map<String, JPanel> containers;

	private Map<String, LayoutHelper> layoutMap = new HashMap<String, LayoutHelper>();

	public ContainerFactory() {
		
		componentFactory = new DefaultComponentFactory();
		layoutMap.put(FLOW_LAYOUT, new FlowLayoutHelper());
		layoutMap.put(BORDER_LAYOUT, new BorderLayoutHelper());
		layoutMap.put(LABEL_COMPONENT_LAYOUT, new LabelComponentLayoutHelper());
		layoutMap.put(FLEXIGRID_LAYOUT, new FlexiGridLayoutHelper());
		
	}

	public JPanel buildContainer(UiModel model) {
		containers = new HashMap<String, JPanel>();
		JPanel rootPane = buildClassLavel(model.getModelBean(), model
				.getModelBean().getClass());
		// buildFieldLavel(model.getModelBean(),);
		Map<String, List<Object>> beanMap = model.getBeanMap();
		if(!containers.containsValue(rootPane)){
			containers.put(model.getViewModelId(), rootPane);
		}
		for (String parentName : beanMap.keySet()) {
			for (Object beanName : beanMap.get(parentName)) {
				JPanel panel = buildClassLavel(beanName);
				if (panel != null && containers.containsKey(parentName)) {
					JPanel parent = containers.get(parentName);
					LayoutHelper helper = layoutMap.get(parent
							.getClientProperty(SWINGETS_LAYOUT_HELPER));
					if (helper == null) {
						helper = layoutMap.get(FLOW_LAYOUT);
					}
					helper.addComponent(parent, panel, beanName.getClass()
							.getAnnotation(APanel.class).layoutProperties());
				}
			}
		}
		rootPane.putClientProperty(UiModel.serialVersionUID, model);
		return rootPane;
	}

	private JPanel buildClassLavel(Object bean) {
		if (bean == null) {
			return null;
		}
		return buildClassLavel(bean, bean.getClass());
	}

	private JPanel buildClassLavel(Object bean,
			Class<? extends Object> beanClass) {
		APanel panelMetadata = beanClass.getAnnotation(APanel.class);
		JPanel panel = null;
		if (panelMetadata != null) {
			panel = buildPanel(panelMetadata, bean);
			if (!panelMetadata.name().equals("")) {
				containers.put(panelMetadata.name(), panel);
			}
		}
		buildFieldLavel(bean, beanClass, panel);
		return panel;
	}

	private List<JComponent> buildFieldLavel(Object bean,
			Class<? extends Object> beanClass, JPanel currentPanel) {
		for (Field field : beanClass.getDeclaredFields()) {
			APanel panelMetadata = field.getAnnotation(APanel.class);
			if (panelMetadata != null) {
				JComponent component = buildPanel(panelMetadata, bean);
				addToParent(currentPanel, panelMetadata, component);
			}

			AComponent componentMetadata = field
					.getAnnotation(AComponent.class);
			if (componentMetadata != null) {
				JComponent component = buildComponent(componentMetadata, bean,
						field.getName());

				addToParent(currentPanel, componentMetadata, component);
			}

		}
		return null;
	}

	private void addToParent(JPanel currentPanel, APanel panelMetadata,
			JComponent component) {
		JPanel parent = currentPanel;
		String parentName = panelMetadata.parent();
		if (!parentName.equals("") && containers.containsKey(parentName)) {
			parent = containers.get(parentName);
		}
		LayoutHelper helper = layoutMap.get(parent
				.getClientProperty(SWINGETS_LAYOUT_HELPER));
		if (helper == null) {
			helper = layoutMap.get(FLOW_LAYOUT);
		}
		helper.addComponent(parent, component, panelMetadata);
	}

	private void addToParent(JPanel currentPanel, AComponent componentMetadata,
			JComponent component) {
		JPanel parent = currentPanel;
		String parentName = componentMetadata.parent();
		if (!parentName.equals("") && containers.containsKey(parentName)) {
			parent = containers.get(parentName);
		}
		LayoutHelper helper = layoutMap.get(parent
				.getClientProperty(SWINGETS_LAYOUT_HELPER));
		if (helper == null) {
			helper = layoutMap.get(FLOW_LAYOUT);
		}
		helper.addComponent(parent, component, componentMetadata);
	}

	private JPanel buildPanel(APanel panelMetadata, Object bean) {
		JPanel panel = componentFactory.createPanel(panelMetadata);
		String layoutName = panelMetadata.layout();
		if (layoutName.contains("(")) {
			layoutName = layoutName.substring(0, layoutName.indexOf("("));
		}
		LayoutHelper helper = layoutMap.get(layoutName);
		if (helper == null) {
			layoutName = FLOW_LAYOUT;
			helper = layoutMap.get(layoutName);
		}
		panel.setLayout(helper.getLayoutManager(panelMetadata));
		panel.putClientProperty(SWINGETS_LAYOUT_HELPER, layoutName);
		for (AComponent componentMetadata : panelMetadata.components()) {
			JComponent component = buildComponent(componentMetadata, bean,
					componentMetadata.property());
			helper.addComponent(panel, component, componentMetadata.layout());
		}

		return panel;
	}

	private JComponent buildComponent(AComponent componentlMetadata,
			Object bean, String property) {
		JComponent component = null;
		if (componentlMetadata.type() == Type.PANEL) {
			Field field = BeanUtilities.getFiled(bean, property);
			if (field != null) {
				APanel panel = field.getAnnotation(APanel.class);
				if (panel != null) {
					component = buildPanel(panel, bean);
				} else {
					component = buildClassLavel(BeanUtilities.getProperty(bean,
							property), field.getType());
				}
			}
		} else {
			component = componentFactory.createComponent(new Property(bean,
					property), componentlMetadata);
		}

		return component;
	}

	public IComponentFactory getComponentFactory() {
		return componentFactory;
	}

	public void setComponentFactory(IComponentFactory componentFactory) {
		this.componentFactory = componentFactory;
	}

	public void addLayoutHelper(String layout, LayoutHelper helper) {
		layoutMap.put(layout, helper);
	}
}
